/*******************************************************************************
 * Copyright (c) 2006 CEA List.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 *
 * Contributors:
 *     CEA List - initial API and implementation
 *******************************************************************************/
package com.cea.papyrus.tabbedproperties.composites;

import java.util.Iterator;
import java.util.List;

import org.eclipse.gef.EditPart;
import org.eclipse.gef.commands.Command;
import org.eclipse.gef.commands.CommandStack;
import org.eclipse.gef.commands.CompoundCommand;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IDialogConstants;
import org.eclipse.jface.text.Assert;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
import org.eclipse.swt.events.MouseEvent;
import org.eclipse.swt.events.MouseListener;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.events.SelectionListener;
import org.eclipse.swt.graphics.Image;
import org.eclipse.swt.layout.FormAttachment;
import org.eclipse.swt.layout.FormData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Menu;
import org.eclipse.swt.widgets.MenuItem;
import org.eclipse.swt.widgets.TableItem;
import org.eclipse.ui.views.properties.tabbed.ITabbedPropertyConstants;
import org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetPage;
import org.eclipse.uml2.uml.Activity;
import org.eclipse.uml2.uml.BehavioralFeature;
import org.eclipse.uml2.uml.Interaction;
import org.eclipse.uml2.uml.OpaqueBehavior;
import org.eclipse.uml2.uml.StateMachine;

import com.cea.papyrus.core.PapyrusPlugin;
import com.cea.papyrus.core.commands.MoveInListCommand;
import com.cea.papyrus.core.editor.CommandStackUtils;
import com.cea.papyrus.core.ui.dialogs.ActivityMethodTreeSelectionDialog;
import com.cea.papyrus.core.ui.dialogs.OpaqueBehaviorMethodTreeSelectionDialog;
import com.cea.papyrus.core.ui.dialogs.StateMachineMethodTreeSelectionDialog;
import com.cea.papyrus.core.utils.Debug;
import com.cea.papyrus.core.utils.LookForElement;
import com.cea.papyrus.umlutils.Behavior;
import com.cea.papyrus.umlutils.Element;

/**
 * Composite that displays the methods of an operation.
 * 
 * @author Remi SCHNEKENBURGER
 */
public class MethodsComposite extends DecoratedTableComposite {

	/**
	 * debug channel.
	 */
	protected static int channel=15;

	/**
	 * Default Constructor.
	 */
	public MethodsComposite() {
		super("Methods");
		this.editButtonlistener = new EditButtonlistener();
	}

	/**
	 * key to access UML Data from table items.
	 */ 
	private final static String KEY = "methods_data";
	
	/**
	 * key to access activity image.
	 */
	private final static String ACTIVITY_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"Activity.gif";
	
	/**
	 * key to access opaque behavior image.
	 */
	private final static String OPAQUE_BEHAVIOR_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"OpaqueBehavior.gif";
	
	/**
	 * key to access state machine image.
	 */
	private final static String STATE_MACHINE_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"StateMachine.gif";
	
	/**
	 * key to access interaction image.
	 */
	private final static String INTERACTION_IMAGE =PapyrusPlugin.UML_ICONS_16x16+"Interaction.gif";
	
	final protected static Image EDIT_ELEMENT_IMG = PapyrusPlugin.getImage(PapyrusPlugin.PAPYRUS_ICONS_16x16+"SegmentEdit_16x16.gif");
	
	/**
	 * BehavioralFeature for which methods are displayed.
	 */
	private BehavioralFeature behavioralFeature;
	
	/**
	 * 
	 */
	private Menu menu;
	
	/**
	 * 
	 */
	private MenuItem activityItem, smItem, obItem;
	
	/**
	 * 
	 */
	private SelectionListener activityItemListener, smItemListener, obItemListener;
	
	private TabbedPropertySheetPage tabbedPropertySheetPage;
	
	private Button editButton;
	
	protected MouseListener editButtonlistener;
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#setElement(com.cea.utils.Element)
	 */
	/**
	 * 
	 * 
	 * @param element 
	 */
	@Override
	public void setElement(Element element) {
		super.setElement(element);
		this.behavioralFeature = (BehavioralFeature)element.getUml2Element();
		
	}
	
	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#createContent(org.eclipse.swt.widgets.Composite, org.eclipse.ui.views.properties.tabbed.TabbedPropertySheetWidgetFactory)
	 */
	public Composite createContent(Composite parent, TabbedPropertySheetPage tabbedPropertySheetPage) {
		Composite composite = super.createContent(parent, tabbedPropertySheetPage.getWidgetFactory());
		
		editButton = tabbedPropertySheetPage.getWidgetFactory().createButton(composite, "",  SWT.PUSH);
		editButton.setVisible(true);
		editButton.setImage(EDIT_ELEMENT_IMG);
		editButton.setToolTipText("Edit selected method");
		FormData data = new FormData();
		data.top  = new FormAttachment(0, 0);
		data.right = new FormAttachment(downButton, -ITabbedPropertyConstants.HSPACE);
		editButton.setLayoutData(data);
		editButton.addMouseListener(editButtonlistener);

		
		this.tabbedPropertySheetPage = tabbedPropertySheetPage;
		
		// add an additional menu to the add Button
		createMenuForAddButton();
		
		return composite;
	}
	
	/**
	 * Adds an additional menu to the addButton. So user can choose which element to create
	 */
	private void createMenuForAddButton() {
		menu = new Menu (addButton);
		// add activity menu item
		activityItem = new MenuItem (menu, SWT.PUSH);
		activityItem.setText("Activity");
		activityItem.setImage(PapyrusPlugin.getImage(ACTIVITY_IMAGE));

		activityItem.addSelectionListener(activityItemListener = new AddElementSelectionListener(Activity.class));
		
		// add State Machine menu item
		smItem = new MenuItem (menu, SWT.PUSH);
		smItem.setText("State Machine");
		smItem.setImage(PapyrusPlugin.getImage(STATE_MACHINE_IMAGE));
		smItem.addSelectionListener(smItemListener = new AddElementSelectionListener(StateMachine.class));
		
		// add Opaque Behavior menu item
		obItem = new MenuItem (menu, SWT.PUSH);
		obItem.setText("Opaque Behavior");
		obItem.setImage(PapyrusPlugin.getImage(OPAQUE_BEHAVIOR_IMAGE));
		obItem.addSelectionListener(obItemListener = new AddElementSelectionListener(OpaqueBehavior.class));
		
		addButton.setMenu (menu);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#refresh()
	 */
	@Override
	public void refresh() {
		if( !table.isDisposed()&& (behavioralFeature!=null)){
			// Remove parameters from table 
			table.removeAll();

			// Read and add parameter
			Iterator<org.eclipse.uml2.uml.Behavior> it = behavioralFeature.getMethods().iterator();
			while (it.hasNext()) {
				org.eclipse.uml2.uml.Behavior behavior = it.next();
				TableItem item = new TableItem(table, SWT.BORDER);
				if(behavior instanceof Activity) {
					item.setImage(PapyrusPlugin.getImage(ACTIVITY_IMAGE));
				} else if(behavior instanceof StateMachine) {
					item.setImage(PapyrusPlugin.getImage(STATE_MACHINE_IMAGE));
				} else if(behavior instanceof OpaqueBehavior) {
					item.setImage(PapyrusPlugin.getImage(OPAQUE_BEHAVIOR_IMAGE));
				} else if(behavior instanceof Interaction) {
					item.setImage(PapyrusPlugin.getImage(INTERACTION_IMAGE));
				} else {
					item.setImage(PapyrusPlugin.getImage(""));
				}
				Behavior utilBehavior = new Behavior(behavior);
				item.setData(KEY, utilBehavior);
				item.setText(utilBehavior.getLabel());
			}
		}
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#addButtonPressed()
	 */
	public void addButtonPressed() {
		menu.setVisible(true);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#removeButtonPressed()
	 */
	public void removeButtonPressed() {
		CompoundCommand command = new CompoundCommand(); 
		// get selection and delete it
		TableItem[] tableItems = table.getSelection();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			final Behavior behavior = (Behavior) tableItems[i].getData(KEY);
			Command unsetMethodCommand = new Command("unsetMethodCommand") {
				
				@Override
				public void undo() {
					//internalBehavior = behavior;
					if(behavior != null) {
						behavioralFeature.getMethods().add((org.eclipse.uml2.uml.Behavior)behavior.getUml2Element());
					} else {
						Debug.error(this, "UnSetMethod: element is null", channel);
					}
				}
				
				@Override
				public boolean canUndo() {
					return (behavior != null);
				}
				
				@Override
				public void execute() {
					if(behavior != null) {
						behavioralFeature.getMethods().remove(behavior.getUml2Element());
					} else {
						Debug.error(this, "UnSetMethod: element is null", channel);
					}
				}
				
				@Override
				public void redo() {
					execute();
				}
			};

			command.add(unsetMethodCommand);
		}
		
		// Execute command
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(command);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#downButtonPressed()
	 */
	public void downButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();

		for(int i = (tableItems.length-1); i >= 0; i--) {
			// get methods list
			List methods = behavioralFeature.getMethods();
			// get method
			org.eclipse.uml2.uml.Behavior behavior 
			= ((Behavior) tableItems[i].getData(KEY)).getUml2Behavior();
			int newIndex = table.indexOf(tableItems[i]) + 1;
			if(newIndex < methods.size()) {
				//new MoveInListCommand(parameters, parameter, newIndex).execute();
				compoundCommand.add(new MoveInListCommand(methods, behavior, newIndex));
			}
		}
		// Execute command
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#upButtonPressed()
	 */
	public void upButtonPressed() {
		CompoundCommand compoundCommand = new CompoundCommand();
		TableItem[] tableItems = table.getSelection();
		List methods = behavioralFeature.getMethods();
		for(int i = 0; i < tableItems.length ; i++) {
			// get parameter
			org.eclipse.uml2.uml.Behavior behavior 
			= ((Behavior) tableItems[i].getData(KEY)).getUml2Behavior();
			int newIndex = table.indexOf(tableItems[i]) - 1;
			if (newIndex >= 0) {
				compoundCommand.add(new MoveInListCommand(methods, behavior, newIndex));
			}
		}
		// Execute command
		CommandStack stack = CommandStackUtils.getCommandStack();
		Assert.isNotNull(stack, "Impossible to adapt current editor into a CommandStack");
		stack.execute(compoundCommand);
	}

	/* (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#editItem(org.eclipse.swt.widgets.TableItem)
	 */
	public void editItem(TableItem item) {
		//TODO: find a solution to load a new property page
		org.eclipse.uml2.uml.Behavior umlElement = ((Behavior)item.getData(KEY)).getUml2Behavior();
		Iterator<EditPart> iter = LookForElement.getAllOutlineEditParts().iterator();
		EditPart targetEditPart = null;
		while(iter.hasNext()){
			EditPart currentEditPart = iter.next();
			if(currentEditPart.getModel().equals(umlElement))
				targetEditPart = currentEditPart;
		}
		
		if (targetEditPart != null)
			tabbedPropertySheetPage.selectionChanged(getActiveEditor(), new StructuredSelection(targetEditPart)) ;

		
	}
	
	/*
	 * (non-Javadoc)
	 * @see com.cea.papyrus.core.ui.composites.DecoratedTableComposite#dispose()
	 */
	public void dispose(){
		super.dispose();
		if(activityItem!=null && !activityItem.isDisposed())
			activityItem.removeSelectionListener(activityItemListener);
		if(smItem!=null && !smItem.isDisposed())
			smItem.removeSelectionListener(smItemListener);
		if(obItem!=null && !obItem.isDisposed())
			obItem.removeSelectionListener(obItemListener);
	}
	
	
	/**
	 * Listener for the menu items.
	 */
	public class AddElementSelectionListener implements SelectionListener {
		
		/**
		 * 
		 * 
		 * @param elementClass 
		 */
		public AddElementSelectionListener(Class elementClass) {
			this.elementClass = elementClass;
		}
		
		/**
		 * 
		 */
		public Class elementClass;
		
		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetDefaultSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetDefaultSelected(SelectionEvent e) {
		}

		/* (non-Javadoc)
		 * @see org.eclipse.swt.events.SelectionListener#widgetSelected(org.eclipse.swt.events.SelectionEvent)
		 */
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void widgetSelected(SelectionEvent e) {
			
			
			Debug.debug(this, "create a "+elementClass, channel);

			int result=42;
			
			Dialog dialog = null;
			
			
			// propose existing behaviors
			if(elementClass.equals(OpaqueBehavior.class)){
				dialog = new OpaqueBehaviorMethodTreeSelectionDialog(composite.getShell(), behavioralFeature);
				result = dialog.open();
			}
			else if(elementClass.equals(Activity.class)){
				dialog = new ActivityMethodTreeSelectionDialog(composite.getShell(), behavioralFeature);
				result = dialog.open();
			}
			else if(elementClass.equals(StateMachine.class)){
				dialog = new StateMachineMethodTreeSelectionDialog(composite.getShell(), behavioralFeature);
				result = dialog.open();
			}
			
			// result treatment
			if(result==IDialogConstants.OK_ID){
				org.eclipse.uml2.uml.Behavior behavior = null;
				if(elementClass.equals(OpaqueBehavior.class)){
					behavior = (org.eclipse.uml2.uml.Behavior)((OpaqueBehaviorMethodTreeSelectionDialog)dialog).getResult();
				}
				else if(elementClass.equals(Activity.class)){
					behavior = (org.eclipse.uml2.uml.Behavior)((ActivityMethodTreeSelectionDialog)dialog).getResult();
				}
				else if(elementClass.equals(StateMachine.class)){
					behavior = (org.eclipse.uml2.uml.Behavior)((StateMachineMethodTreeSelectionDialog)dialog).getResult();
				}
				
				final org.eclipse.uml2.uml.Behavior internalBehavior = behavior;
				
				if(behavior!=null){
					Command setMethodCommand = new Command("setMethodCommand") {
						
						@Override
						public void execute() {
							//internalBehavior = behavior;
							if(internalBehavior != null) {
								behavioralFeature.getMethods().add(internalBehavior);
							} else {
								Debug.error(this, "SetMethod: element is null", channel);
							}
						}
						
						@Override
						public boolean canUndo() {
							return (internalBehavior != null);
						}
						
						@Override
						public void undo() {
							if(internalBehavior != null) {
								behavioralFeature.getMethods().remove(internalBehavior);
							} else {
								Debug.error(this, "SetMethod: element is null", channel);
							}
						}
						
						@Override
						public void redo() {
							execute();
						}
					};
					
					// 4. Run set command
					CommandStack commandStack = CommandStackUtils.getCommandStack();
					if(commandStack!= null) {
						commandStack.execute(setMethodCommand);
					}
					
					refresh();
				}

			}
		}
	}
	
	private class EditButtonlistener implements MouseListener {
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDoubleClick(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseDown(MouseEvent e) {
			// do nothing
		}
		
		/**
		 * 
		 * 
		 * @param e 
		 */
		public void mouseUp(MouseEvent e) {
			TableItem[] tableItems = table.getSelection();
			if(tableItems.length>0)
				editItem(tableItems[0]);
		}
	}

	
}
